package cn.mw.cmdb.config.MQTT;

import cn.mw.cmdb.config.MQTTProviderConfig;
import cn.mw.cmdb.entity.InstanceInfo;
import cn.mw.cmdb.param.MwCabinetLayoutDataBaseParam;
import cn.mw.cmdb.param.V5008LabelStateParam;
import cn.mw.cmdb.param.V5008LabelStateUModuleInfo;
import cn.mw.cmdb.param.V5008OpeAckParam;
import cn.mw.cmdb.service.impl.CmdbServiceManage;
import cn.mw.cmdb.service.impl.InstanceServiceImpl;
import cn.mw.cmdb.service.impl.UNumManageServiceImpl;
import cn.mw.microMonitorCommon.util.SpringUtils;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.mw.cmdb.service.impl.InstanceServiceImpl.tagUManageIdKey;
import static cn.mw.cmdb.util.ValConvertUtil.intValueConvert;
import static cn.mw.cmdb.util.ValConvertUtil.strValueConvert;
import static cn.mw.cmdb.viewProcessor.ModelViewUNumProcess.deviceUIndex;

/**
 * mqtt返回消息处理
 * V5008协议
 */
@Slf4j
public class MessageArrivedByV5008 implements MessageArrivedHandle {
    private static final String V5008Key = "V5008Upload/";
    public static final String opeAckKey = "/OpeAck";


    //获取所有心跳包请求
    private static final String opeAckUrl = V5008Key + "+" + opeAckKey;


    @Override
    public void messageManage(String topic, Object message) throws Exception {
        byte[] payload = (byte[]) message;
        StringBuilder hexString = new StringBuilder();
        for (int number : payload) {
            hexString.append(String.format("%02X", number & 0xFF));
        }
        String data = hexString.toString(); // Split into pairs of characters
        //心跳接口
        if (topic != null && topic.contains(opeAckKey)) {
            opeAckStateSync(topic, data);
        }
        //资产变更
        if (topic != null && topic.contains(LabelStateKey)) {
            deviceUNumCheck(topic, data);
        }
        log.info("MessageArrivedByV5008::format" + data);
    }

    /**
     * 根据获取的变更信息，匹配数据库中的设备U位信息是否正确
     * 同时根据匹配结果，设置上传机柜U位指示灯
     */
    public void deviceUNumCheck(String topic, String data) throws Exception {
        //网关ID
        String gatewayId = topic.replace(V5008Key, "").replace(LabelStateKey, "");
        V5008LabelStateParam labelStateParam = changeLabelState(data);
        String rs485Address = strValueConvert(labelStateParam.getRS485Address());
        List<V5008LabelStateUModuleInfo> uModuleInfoList = labelStateParam.getUModuleInfoList();
        List<String> tagIds = uModuleInfoList.stream().map(s -> strValueConvert(s.getTagId())).collect(Collectors.toList());
        Map<Integer, Integer> collect = uModuleInfoList.stream().collect(Collectors.toMap(s -> s.getTagId(), s -> s.getUNum()));
        CmdbServiceManage cmdbServiceManage = SpringUtils.getBean(CmdbServiceManage.class);
        InstanceServiceImpl instanceService = (InstanceServiceImpl) cmdbServiceManage.getModelService(InstanceServiceImpl.ID);
        //获取标签Id对应的实例设备
        List<InstanceInfo> instanceInfoList = instanceService.doListByTagIdsNotAuth(tagIds);
        List<MwCabinetLayoutDataBaseParam> cabinetUNumStateList = new ArrayList<>();
        List<InstanceInfo> errorList = new ArrayList<>();
        MessageSource messageSource = SpringUtils.getBean(MessageSource.class);

        for (InstanceInfo instanceInfo : instanceInfoList) {
            MwCabinetLayoutDataBaseParam cabinetUNumState = new MwCabinetLayoutDataBaseParam();
            //标签管理Id
            String tagId = strValueConvert(instanceInfo.getData().get(tagUManageIdKey));
            //设备U位数
            int UNum = intValueConvert(instanceInfo.getData().get(deviceUIndex));
            if (collect != null && collect.containsKey(tagId)) {
                //U位管理系统返回的U位数
                Integer tagNum = collect.get(tagId);
                cabinetUNumState.setIndex(tagNum);
                if (tagNum.intValue() == UNum) {
                    //正确 颜色：绿色-4
                    cabinetUNumState.setStatus(UIndexUsedOKState);
                } else {
                    //上架U位数匹配不上，错误，颜色：红闪烁-8
                    cabinetUNumState.setStatus(UIndexUsedFailState);
                    String message = messageSource.getMessage("uNum-manage-error-message", null, LocaleContextHolder.getLocale());
                    instanceInfo.setUManageTipMessage(message);
                    errorList.add(instanceInfo);
                }
                cabinetUNumStateList.add(cabinetUNumState);
            }
        }
        //请求：E1(命令码)+U 位监测模块 RS485 地址(1byte)+[U 位模块上数字 U(1byte)+颜色码(1byte)]*n
        //发布消息，主动上传设置U位指示灯
        sendUManageLightState(gatewayId, rs485Address, cabinetUNumStateList);
        if (CollectionUtils.isNotEmpty(errorList)) {
            //TODO U位管理错误时，需要通知提醒

        }
    }

    /**
     * 发送U位管理指示灯状态消息
     */
    private void sendUManageLightState(String gatewayId, String RS485Address, List<MwCabinetLayoutDataBaseParam> cabinetUNumStateList) {
        //请求：E1(命令码)+U 位监测模块 RS485 地址(1byte)+[U 位模块上数字 U(1byte)+颜色码(1byte)]*n
        //发布消息，主动上传设置U位指示灯
        MQTTProviderConfig mqttProviderConfig = SpringUtils.getBean(MQTTProviderConfig.class);
        //发布的主题
        String publishTopic = V5008Key + gatewayId;
        //发布消息
        String publishData = "";
        //消息前缀
        String publishPrefix = "E1" + RS485Address;
        StringBuilder sbr = new StringBuilder();
        for (MwCabinetLayoutDataBaseParam baseParam : cabinetUNumStateList) {
            String UIndexStr = convertHexString(baseParam.getIndex());
            String UStateStr = convertHexString(baseParam.getStatus());
            sbr.append(UIndexStr).append(UStateStr);
        }
        String dataStr = sbr.toString();
        publishData = publishPrefix + dataStr;
        mqttProviderConfig.publish(publishTopic, publishData);
    }

    public static V5008LabelStateParam changeLabelState(String data) {
        // 确认数据头是否为BB
        if (!data.startsWith("BB")) {
            log.error("Invalid data format");
            return null;
        }
        // 去掉数据头BB
        data = data.substring(2);
        //BB + U位监测模块RS485地址(1byte)+U位监测模块ID(4bytes)+预留(1byte)+U位总数(1byte)+上报的U数(1byte)+[U位模块上数字U(1byte)+有无告警(1byte)+标签ID(4bytes)]*n
        // 解析固定部分
        //U位RS485地址
        String rs485AddressHex = data.substring(0, 2);
        //U位监测模块Id
        String UMonitorModelIdHex = data.substring(2, 10);
        //预留位置
        String reserved = data.substring(10, 12);
        //U位总数
        String totalUCountHex = data.substring(12, 14);
        //上报U数
        String reportedUCountHex = data.substring(14, 16);
        V5008LabelStateParam labelStateParam = new V5008LabelStateParam();
        long rs485Address = Long.parseLong(rs485AddressHex, 16);
        int UMonitorModelId = Integer.parseInt(UMonitorModelIdHex, 16);
        int totalUCount = Integer.parseInt(totalUCountHex, 16);
        int reportedUCount = Integer.parseInt(reportedUCountHex, 16);
        labelStateParam.setRS485Address(rs485Address);
        labelStateParam.setUMonitorModelId(UMonitorModelId);
        labelStateParam.setUCountNum(totalUCount);
        labelStateParam.setReportUCount(reportedUCount);
        List<V5008LabelStateUModuleInfo> list = new ArrayList<>();
        // 解析可变部分
        String variablePart = data.substring(16);
        int offset = 0;
        for (int i = 0; i < reportedUCount; i++) {
            V5008LabelStateUModuleInfo uModuleInfo = new V5008LabelStateUModuleInfo();
            String UNumHex = variablePart.substring(offset, offset + 2);
            String alarmStatus = variablePart.substring(offset + 2, offset + 4);
            String tagIdHex = variablePart.substring(offset + 4, offset + 12);
            offset += 12;
            int UNum = Integer.parseInt(UNumHex, 16);
            uModuleInfo.setUNum(UNum);
            //00表示无告警，01有告警
            uModuleInfo.setAlarmStatus("01".equals(alarmStatus) ? true : false);
            int tagId = Integer.parseInt(tagIdHex, 16);
            uModuleInfo.setTagId(tagId);
            list.add(uModuleInfo);
        }
        labelStateParam.setUModuleInfoList(list);
        return labelStateParam;
    }

    /**
     * 心跳状态同步
     * 机柜连接状态
     */
    public void opeAckStateSync(String topic, String message) throws Exception {
        //网关ID
        String gatewayId = topic.replace(V5008Key, "").replace(opeAckKey, "");
        try {
            //根据利用U位管理模块Id，更新数据库中机柜的连接状态
            CmdbServiceManage cmdbServiceManage = SpringUtils.getBean(CmdbServiceManage.class);
            UNumManageServiceImpl uNumManageService = (UNumManageServiceImpl) cmdbServiceManage.getModelService(UNumManageServiceImpl.ID);
            //V5008心跳包数据解析
            List<V5008OpeAckParam> list = opeAckDataAnalysis(message);
            Set<String> collect = list.stream().filter(s -> !Strings.isNullOrEmpty(s.getUMonitorModelId()) && !"0".equals(s.getUMonitorModelId())).map(s -> s.getUMonitorModelId()).collect(Collectors.toSet());
            //同步机柜在线状态
            setRedisUMangeKey(gatewayId, collect);
        } catch (Exception e) {
            log.error("opeAckStateSync to fail::", e);
        }

    }

    public static void main(String[] args) {
        opeAckDataAnalysis("CC010000000000020000000000037151519E2A040000000000");
    }

    /**
     * V5008心跳包数据解析
     *
     * @param message
     * @return
     */
    private static List<V5008OpeAckParam> opeAckDataAnalysis(String message) {
        List<V5008OpeAckParam> list = new ArrayList<>();
        //对返回的数据解析
        //固定格式，响应：CC（或 CB，U 位监测模块上线或 U 位总数变化时为 CB）+ [U 位监测模块 RS485地址(1byte)+U 位监测模块 ID（4bytes）+U 位监测模块 U 位数(1byte)]*5，其中5表示5组U位监测模块。
        if (message != null && message.startsWith("CC") || message.startsWith("CB")) {
            // 去掉前两位的"CC"
            message = message.substring(2);
            // 每组数据长度
            int groupLength = 12; // 6字节 -> 每字节2个十六进制字符，共12个字符
            // 循环解析每组数据
            for (int i = 0; i < message.length(); i += groupLength) {
                String group = message.substring(i, i + groupLength);
                V5008OpeAckParam opeAckParam = parseOpeAck(group);
                list.add(opeAckParam);
            }
        }
        return list;
    }

    /**
     * 心跳包解析规则
     *
     * @param group
     * @return
     */
    private static V5008OpeAckParam parseOpeAck(String group) {
        V5008OpeAckParam opeAckParam = new V5008OpeAckParam();
        // RS485 地址：第一个字节（前2个字符）
        String rs485AddressStr = group.substring(0, 2);
        // ID：接下来4个字节（8个字符）
        String id = group.substring(2, 10);
        // U 位数：最后1个字节（2个字符）
        String uCount = group.substring(10, 12);
        // 将U位数从十六进制转成十进制
        int rs485Address = Integer.parseInt(rs485AddressStr, 16);
        String uMonitorModelId = parseDecString(id);
        int uCountNum = Integer.parseInt(uCount, 16);
        opeAckParam.setRS485Address(strValueConvert(rs485Address));
        opeAckParam.setUMonitorModelId(uMonitorModelId);
        opeAckParam.setUCountNum(uCountNum);
        return opeAckParam;
    }

    /**
     * 16进制转10进制 long类型
     *
     * @param dataStr
     * @return
     */
    private long parseDecLong(String dataStr) {
        long data = Long.parseLong(dataStr, 16);
        return data;
    }

    /**
     * 16进制转10进制 字符串类型
     *
     * @param dataStr
     * @return
     */
    private static String parseDecString(String dataStr) {
        String data = strValueConvert(Long.parseLong(dataStr, 16));
        return data;
    }

    /**
     * 10进制转16进制 字符串类型
     *
     * @param decimal
     * @return
     */
    private String convertHexString(Object decimal) {
        if (decimal == null) {
            return "";
        }
        String data = String.format("%02X", strValueConvert(decimal));
        return data;
    }

}
